Logistic regression

로지스틱 회귀
퍼셉트론은 클래스가 선형적으로 구분되지 않을 때, 절대 수렴할 수 없다.
로지스틱 회귀(Logistic regression)은 선형 이진 분류에 더욱 강력하다.
(로지스틱 회귀는 회귀가 아닌 분류 모델이다)

로지스틱 회귀 이진 분류를 위한 선형 모델이지만, 다중 클래스 설정으로 일반화할 수 있다.
다항 로지스틱 회귀(multinomial logistic regression) 또는 소프트맥스 회귀(softmax regression)라고 부른다.
해커에게 전해들은 머신러닝 #2
logit 와 Z의 선형 관계를 이용하면 logit(P(y=1)|x))=w0x0+w1x1+w2x2+ … + wmxm=w.Tx=Z 로 나타낼 수 있다.
import matplotlib.pyplot as plt
import numpy as np
def sigmoid(z):
return 1.0/(1.0+np.exp(-z))
z=np.arange(-7, 7, 0.1)
phi_z=sigmoid(z)
plt.plot(z,phi_z)
plt.axvline(0.0, color='k')
plt.ylim(-0.1, 1.1)
plt.xlabel('z')
plt.ylabel('$\phi (z)$')
plt.yticks([0.0, 0.5, 1.0])
ax=plt.gca()
ax.yaxis.grid(True)
plt.tight_layout()
plt.show()
로지스틱 회귀에서는 위와 같은 시그모이드 함수를 활성화 함수로 사용한다.
로지스틱 함수는 클래스 레이블을 예측하는 것 외에 클래스에 소속될 확률을 추정하는데 유용하다.
로지스틱 함수의 가능도(Likelihood)
3. Maximun Likelihood Estimation, Logistic Regression, Softmax Regression
위의 가능도 함수에 로그(자연)함수를 취한 함수를 로그 기능도 함수라고 한다.
로그함수를 취함으로써 가능도가 매우 작을 때 발생하는 수치상의 언더플로(underflow)를 미연에 방지할 수 있다.
또한 계수의 곱을 계수의 곱으로 바꿀 수 있다.

로지스틱 회귀의 비용함수
def cost_1(z):
return -np.log(sigmoid(z))
def cost_0(z):
return -np.log(1-sigmoid(z))
z=np.arange(-10, 10, 0.1)
phi_z=sigmoid(z)
c1=[cost_1(x) for x in z]
plt.plot(phi_z, c1, label='J(w) y=1')
c0=[cost_0(x) for x in z]
plt.plot(phi_z, c0, linestyle='--', label='J(w) y=0')
plt.ylim(0.0, 5.1)
plt.xlim([0,1])
plt.xlabel('$\phi$(z)')
plt.ylabel('J(w)')
plt.legend(loc='best')
plt.tight_layout()
plt.show()
LogisticRegreesionGD
class LogisticRegressionGD(object):
def __init__(self, eta=0.05, n_iter=100, random_state=1):
self.eta=eta
self.n_iter=n_iter
self.random_state=random_state
def fit(self, X, y):
rgen=np.random.RandomState(self.random_state)
self.w_=rgen.normal(loc=0.0, scale=0.01, size=1+X.shape[1])
self.cost_=[]
for i in range(self.n_iter):
n_input=self.net_input(X)
output=self.activation(net_input)
errors=(y-output)
self.w_[1:]+=self.eta*X.T.dot(errors)
self.w_[0]+=self.eta*errors.sum()
cost=(-y.dot(np.log(output))-((1-y).dot(np.log(1-output))))
self.cost_.append(cost)
return self
def net_input(self,X):
return np.dot(X,self.w_[1:])+self.w_[0]
def activation(self, z):
return 1./(1.+np.exp(-np.clip(z, -250, 250)))
def predict(self, X):
return np.where(self.activation(self.net_input(X))>=0.5, 1, 0)
위의 로지스틱 모델은 이진 분류 문제에만 적용할 수 있다.
X_train_01_subset=X_train[(y_train==0)|(y_train==1)]
y_train_01_subset=y_train[(y_train==0)|(y_train==1)]
lrgd=LogisticRegressionGD(eta=0.05, n_iter=1000, random_state=1)
lrgd.fit(X_train_01_subset, y_train_01_subset)
plot_decision_regions(X=X_train_01_subset, y=y_train_01_subset, classifier=lrgd)
plt.xlabel('petal length [standardized]')
plt.ylabel('petal width [standardized]')
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()
수학적으로 계산하여 보면, 로그 기능도를 최대화하는 것은 아달린의 비용함수를 최소화하는 것과 동일하다.
Logistic regression with Scikit-learn
사이킷런의 로지스틱 회귀 구현은 매우 최적화 되어 있고, 다중 분류도 지원한다.
from sklearn.linear_model import LogisticRegression
lr=LogisticRegression(C=100.0, random_state=1)
lr.fit(X_train_std, y_train)
plot_decision_regions(X_combined_std, y_combined, classifier=lr, test_idx=range(105,150))
plt.xlabel('petal length [standardized]')
plt.ylabel('petal width [standardized]')
plt.legend(loc='upper left')
plt.tight_layout()
plt.show()
사이킷런은 로지스틱 회귀를 위한 다양한 최적화 알고리즘을 제공한다.
solver매개변수에 ‘newtoncg’, lbfgs’, liblinear’, ‘sag’, ‘saga’를 지정할 수 있다

매개변수 기본값은 auto로 이진분류이거나 solver=‘liblinear’DLF RUDDN ovr을 선택
그 이에는 multinomial을 선택한다.
클래스 소속 확률
lr.predict_proba(X_test_std[:3, :])
predict_proba 함수를 이용해서 어떤 클래스에 속할 확률을 계산할 수 있다.
위의 예시는 테스트 데이터셋에 있는 처음 세 개의 샘플 확률을 예측

array([[1.52213484e-12, 3.85303417e-04, 9.99614697e-01],

       [9.93560717e-01, 6.43928295e-03, 1.14112016e-15],

       [9.98655228e-01, 1.34477208e-03, 1.76178271e-17]])

행에서 가장 큰 값의 열이 예측 클래스 레이블이 된다.
lr.predict(X_test_std[:3, :]

array([2, 0, 0])

샘플 하나의 클래스 레이블을 예측할 때, 사이킷런은 입력 데이터로 2차원 배열을 기대한다.
따라서 하나의 행을 2차원 포맷으로 변경해줘야 한다.
lr.predict(X_test_std[0,:].reshape(1,-1))